﻿using Microsoft.AspNetCore.SignalR;
using MultiAgentsClient.ChatDataStructures;
using MultiAgentsServer.Scripts.Database;
using MultiAgentsServer.Scripts.Hubs;
using MultiAgentsServer.Scripts.Services.DataModels;
using MultiAgentsServer.Scripts.Shared;
using MultiAgentsShared;
using System.Text.Json;
using static MultiAgentsServer.Scripts.Database.DatabaseRepository;

namespace MultiAgentsServer.Scripts.Services;

public class BehaviorTreeService
{
    #region Commands: WorkflowSignalRHub -> WorkflowConfigService
    public record CreateNewWorkflowCommand(string ConnectionId, string SessionId, int UserId, string WorkflowName, string WorkflowJson);
    public record UpdateWorkflowCommand(string ConnectionId, string SessionId, int UserId, int WorkflowId, string WorkflowName, string WorkflowJson);
    public record RemoveWorkflowCommand(string ConnectionId, string SessionId, int UserId, int WorkflowId);
    public record GetAllWorkflowsByUserCommand(string ConnectionId, string SessionId, int UserId);
    public record SendSystematicNotificationCommand(int UserId, string ConnectionId, SystematicNotificationsDB? NotificationsDB = null);
    public record WorkflowProccessedRequestCommand(string ConnectionId, string SessionId, int UserId, int NotificationId);
    public record LoadWorkflowByJsonRequestCommand(string ConnectionId, string SessionId, int UserId, string WorkflowJson);
    #endregion

    private readonly IEventBus eventBus;
    private readonly DataBaseOperationService _dataBaseOperationService;
    private readonly DatabaseRepository databaseRepository;
    private readonly IHubContext<SignalRWorkflowHub> workflowContext;
    private readonly UserCookieDataModel userCookieDataModel;
    private readonly LlmComEntryPoint _llmComEntryPoint;

    public BehaviorTreeService(
        IEventBus eventBusIn, 
        DatabaseRepository databaseRepository,
        LlmComEntryPoint llmComEntryPoint,
        IHubContext<SignalRWorkflowHub> signalRWorkflowHubContext,
        DataBaseOperationService dataBaseOperationService,
        UserCookieDataModel userCookieDataModel)
    {
        this.userCookieDataModel = userCookieDataModel;

        eventBus = eventBusIn;
        workflowContext = signalRWorkflowHubContext;
        _dataBaseOperationService = dataBaseOperationService;
        _llmComEntryPoint = llmComEntryPoint;
        this.databaseRepository = databaseRepository;

        eventBus.Subscribe<CreateNewWorkflowCommand>(command => { Task.Run(() => CreateNewWorkflow(command)); });
        eventBus.Subscribe<UpdateWorkflowCommand>(command => { Task.Run(() => UpdateWorkflow(command)); });
        eventBus.Subscribe<RemoveWorkflowCommand>(command => { Task.Run(() => RemoveWorkflow(command)); });
        eventBus.Subscribe<GetAllWorkflowsByUserCommand>(command => { Task.Run(() => GetAllWorkflowsByUser(command)); });
        eventBus.Subscribe<WorkflowProccessedRequestCommand>(command => { Task.Run(() => UpdateSystematicNotificationStatus(command)); });
        eventBus.Subscribe<LoadWorkflowByJsonRequestCommand>(command => { Task.Run(() => LoadWorkflowByJsonRequestHandlerAsync(command)); });
        eventBus.Subscribe<SendSystematicNotificationCommand>(command => { Task.Run(() => SendSystematicNotificationHandlerAsync(command)); });
        eventBus.Subscribe<ServerNodeLLMSwitchRequestCommand>(command => { Task.Run(() => ServerNodeLLMSwitchRequestCommandHandlerAsync(command)); });
        eventBus.Subscribe<ServerNodeParameterExtractionRequestCommand>(command => { Task.Run(() => ServerNodeParameterExtractionRequestCommandHandlerAsync(command)); });
    }

    public async Task CreateNewWorkflow(CreateNewWorkflowCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            WorkflowDB workflowDb = new()
            {
                OwnerId = cmd.UserId,
                Name = cmd.WorkflowName,
                WorkflowJson = cmd.WorkflowJson
            };
            
            int workflowId = databaseRepository.AddRow(DataBaseTableNames.WorkflowConfigs.ToString(), workflowDb);

            await SignalRWorkflowHub.CreateNewWorkflowResponse(workflowContext, cmd.ConnectionId, workflowId, cmd.UserId, cmd.WorkflowName, cmd.WorkflowJson);
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to add or update workflow: {ex.Message}");
            throw;
        }
    }

    public async Task UpdateWorkflow(UpdateWorkflowCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            WorkflowDB workflowDb = new()
            {
                Id = cmd.WorkflowId,
                OwnerId = cmd.UserId,
                Name = cmd.WorkflowName,
                WorkflowJson = cmd.WorkflowJson
            };

            bool success = databaseRepository.UpdateRowById(DataBaseTableNames.WorkflowConfigs.ToString(), workflowDb);

            await SignalRWorkflowHub.UpdateWorkflowResponse(workflowContext, cmd.ConnectionId, cmd.WorkflowId, cmd.UserId, cmd.WorkflowName, cmd.WorkflowJson);
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to add or update workflow: {ex.Message}");
            throw;
        }
    }

    public async Task RemoveWorkflow(RemoveWorkflowCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            WorkflowDB workflowDB = new()
            {
                Id = cmd.WorkflowId,
                OwnerId = cmd.UserId,
            };

            bool success = databaseRepository.DeleteRow(DataBaseTableNames.WorkflowConfigs.ToString(), workflowDB);

            if (success)
            {
                await SignalRWorkflowHub.RemoveWorkflowResponse(workflowContext, cmd.ConnectionId, cmd.WorkflowId);
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to remove workflow: {ex.Message}");
            throw;
        }
    }

    public async Task GetAllWorkflowsByUser(GetAllWorkflowsByUserCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            WorkflowDB workflowDB = new()
            {
                OwnerId = cmd.UserId,
            };

            List<WorkflowDB> workflowList = databaseRepository.QueryRows(DataBaseTableNames.WorkflowConfigs.ToString(), workflowDB);
            List<Workflow> workflows = [];
            foreach (WorkflowDB workflowListElement in workflowList)
            {
                workflows.Add(new Workflow()
                {
                    Id= workflowListElement.Id,
                    OwnerId= workflowListElement.OwnerId,
                    Name= workflowListElement.Name,
                    WorkflowJson= workflowListElement.WorkflowJson,
                });
            }

            await SignalRWorkflowHub.GetAllWorkflowsByUserResponse(workflowContext, cmd.ConnectionId, workflows);
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to retrieve workflows for user {cmd.UserId}: {ex.Message}");
            throw;
        }
    }

    public void UpdateSystematicNotificationStatus(WorkflowProccessedRequestCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;

        }
        try
        {
            SystematicNotificationsDB systematicNotificationsDB = new()
            {
                Id = cmd.NotificationId,
                IsProcessed = 1,
            };

            bool success = databaseRepository.UpdateRowById(DataBaseTableNames.SystematicNotifications.ToString(), systematicNotificationsDB);
            if (success == false)
            {
                throw Debug.ShouldNotHappen();
            }
        }
        catch (Exception ex)
        {
            Debug.LogError($"Failed to retrieve workflows for user: {ex.Message}");
            throw;
        }
    }

    private async Task LoadWorkflowByJsonRequestHandlerAsync(LoadWorkflowByJsonRequestCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        WorkflowDB newWorkflow = new()
        {
            OwnerId = cmd.UserId,
            Name = "New Workflow",
            WorkflowJson = cmd.WorkflowJson,
        };

        int newWorkflowId = databaseRepository.AddRow(DataBaseTableNames.WorkflowConfigs.ToString(), newWorkflow);

        await SignalRWorkflowHub.CreateNewWorkflowResponse(workflowContext, cmd.ConnectionId, newWorkflowId, cmd.UserId, newWorkflow.Name, newWorkflow.WorkflowJson);
        
    }

    private async Task SendSystematicNotificationHandlerAsync(SendSystematicNotificationCommand cmd)
    {
        List<SystematicNotificationsDB> notificationsDBList = [];
        if (cmd.NotificationsDB == null)
        {
            notificationsDBList = _dataBaseOperationService.GetAllSystematicNotificationsByUserId(cmd.UserId);
        }
        else
        {
            notificationsDBList.Add(cmd.NotificationsDB);
        }

        List<WorkflowNotification> systematicNotificationsList = [];
        foreach (SystematicNotificationsDB notification in notificationsDBList)
        {
            UserSubscriptionsDB userSubscriptionInfo = _dataBaseOperationService.GetUserSubscriptionInfoById(notification.SubscriptionId);
            string workflowName = _dataBaseOperationService.GetWorkflowNameById(userSubscriptionInfo.WorkflowId);

            WorkflowNotification systematicNotification = new()
            {
                id = notification.Id,
                workflowName = workflowName,
                workflowId = userSubscriptionInfo.WorkflowId,
                content = notification.Content,
                repositoryAddressUserSubscribed = userSubscriptionInfo.Address,
                isProcessed = notification.IsProcessed == 1,
                requireApproval = userSubscriptionInfo.ShouldAskMe == 1,
                subscriptionClass = userSubscriptionInfo.Class,
            };

            systematicNotificationsList.Add(systematicNotification);
        }

        if (systematicNotificationsList.Count > 0)
        {
            await SignalRWorkflowHub.SendSystematicNotification(workflowContext, cmd.ConnectionId, systematicNotificationsList);
        }
    }

    private async Task ServerNodeLLMSwitchRequestCommandHandlerAsync(ServerNodeLLMSwitchRequestCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            LlmStreamSlice? llmSwitchResponse = await _llmComEntryPoint.RequestChatSimp(PromptMessagesConsts.WorkflowSwitchNodeMessages("", cmd.Message, cmd.Categories), DefaultModelConfigModel.DefaultModelConfig.LLMSwitchNodeModel);

            JsonDocument jsonDocument = JsonDocument.Parse(llmSwitchResponse?.message ?? "");

            JsonElement root = jsonDocument.RootElement;


            int categoryId;
            string categoryName;
            categoryId = Convert.ToInt32(root.GetProperty("category_id").GetString() ?? "0");
            categoryName = root.GetProperty("category_name").GetString() ?? "";

            if (cmd.Categories.Contains(categoryName) == false)
            {
                if (categoryId >= 0 && categoryId < cmd.Categories.Count)
                {
                    categoryName = cmd.Categories[categoryId];
                }
                else
                {
                    categoryName = cmd.Categories[0];
                }
            }

            await SignalRWorkflowHub.ServerNodeLLMSwitchApiResponse(workflowContext, cmd.ConnectionId, cmd.NodeId, categoryName);
        }
        catch (Exception ex)
        {
            Debug.LogError(ex?.ToString() ?? "Unknown error");
            await SignalRWorkflowHub.ServerNodeLLMSwitchApiResponse(workflowContext, cmd.ConnectionId, cmd.NodeId, cmd.Categories[0]);
        }
    }

    private async Task ServerNodeParameterExtractionRequestCommandHandlerAsync(ServerNodeParameterExtractionRequestCommand cmd)
    {
        if (userCookieDataModel.CheckUserAndSession(cmd.UserId, cmd.SessionId) == false)
        {
            return;
        }

        try
        {
            List<string> values = [];
            foreach (string description in cmd.Descriptions)
            {
                LlmStreamSlice? llmSwitchResponse = await _llmComEntryPoint.RequestChatSimp(PromptMessagesConsts.WorkflowParameterExtractionMessages("", cmd.Context, description), DefaultModelConfigModel.DefaultModelConfig.ParameterExtractionNodeModel);

                values.Add(llmSwitchResponse?.message ?? "");
            }

            await SignalRWorkflowHub.ServerNodeParameterExtractionApiResponse(workflowContext, cmd.ConnectionId, cmd.NodeId, values);
        }
        catch (Exception ex)
        {
            Debug.LogError(ex?.ToString() ?? "Unknown error");
            await SignalRWorkflowHub.ServerNodeParameterExtractionApiResponse(workflowContext, cmd.ConnectionId, cmd.NodeId, []);
        }
    }
}
